package com.wencai.daily;

import com.wencai.bean.BuyStock;
import com.wencai.bean.CalculateResult;
import com.wencai.bean.PriceAction;
import com.wencai.bean.StockData;
import com.alibaba.excel.util.CollectionUtils;
import com.wencai.daily.bean.DailyData;
import com.wencai.daily.bean.FiveMinutesBuyAndSell;
import com.wencai.daily.bean.FiveMinutesData;
import com.wencai.daily.utils.ReadUtils;
import org.jetbrains.annotations.NotNull;
import com.wencai.util.*;

import java.io.IOException;
import java.math.BigDecimal;
import java.math.RoundingMode;
import java.util.*;
import java.util.function.BiFunction;

/**
 * @author wangpan
 * @date 2023/6/1
 */
public class Main {
    private static String currentYear = "2022";


    private static String fiveMinutesDataDir = "D:\\Users\\Administrator\\Desktop\\日线策略\\5分线数据" + currentYear + "\\";

    private static String filterDir = "D:\\Users\\Administrator\\Desktop\\日线策略\\过滤数据汇总\\";

    private static String staticDir = "D:\\Users\\Administrator\\Desktop\\日线策略\\统计数据汇总\\" + currentYear + "\\";

    private static String outPath = "D:\\Users\\Administrator\\Desktop\\日线策略\\买卖细节.xlsx";

    private static int downLimit = -10;
    private static int upLimit = 10;
    private static boolean buyAtLastMinutes = true;
    private static boolean fixSellPoint = true;
    private static String sellPoint = "1130";


    // 手续费损耗
    private static double feeLoss = 0.00132;

    private static double START_MONEY = 100000;


    public static void main(String[] args) throws IOException {
//        double amountRate = 1.1;
//        String buyTime = "1420";
//        String sellTime = "1330";
//        List<BuyStock> buyStockList = getBuyStocks(amountRate, buyTime, sellTime);
//        CalculateResult singleResult = TimeLoopUtils.getCalculateResult(buyTime, sellTime, buyStockList);
//        System.out.println(singleResult);
//        ExportUtils.exportBuyStock(buyStockList, BuyStock.class, outPath);

        BiFunction<List<DailyData>, Double, DailyData> biFunction = StockChooser::chooseDownToTenDayAvg;
        BuyStrategy buyStrategy = new BuyStrategy(-1, BuyStrategyType.NORMAL);
        SellStrategy sellStrategy = new SellStrategy(8, -1, SellStrategyType.HIGHER);
        for (double i = -7; i <= 0; i = i + 0.5) {
            System.out.println(i);
            double loop = loop(biFunction, i, buyStrategy, sellStrategy);
        }
    }

    private static double loop(BiFunction<List<DailyData>, Double, DailyData> biFunction, double param, BuyStrategy buyStrategy, SellStrategy sellStrategy) throws IOException {
        double max = -1;
        param = new BigDecimal(param).setScale(2, RoundingMode.HALF_UP).doubleValue();
        List<CalculateResult> resultList = getResult(param, biFunction, PriceAction.FIVE_MINUTES_CLOSE, buyStrategy, PriceAction.FIVE_MINUTES_CLOSE, sellStrategy);

        StringBuilder builder = new StringBuilder();
        Double average = resultList.stream().mapToDouble(CalculateResult::getGainRate).average().orElse(0);
        builder.append("average result is :" + average).append(System.lineSeparator());

        resultList.sort(Comparator.comparingDouble(CalculateResult::getGainRate));
        Collections.reverse(resultList);
        builder.append("maxList:").append(System.lineSeparator());
        builder.append(resultList.get(0)).append(System.lineSeparator());
        System.out.println(builder.toString());
//        IOUtils.writeFile(staticDir +  + ".txt", builder.toString());

        double gainRate = resultList.get(0).getGainRate();
        if (gainRate > max) {
            max = gainRate;
        }

//        ExportUtils.exportCalculateResult(resultList, CalculateResult.class, statisticDir + "统计.xlsx");
        return max;
    }

    @NotNull
    private static List<CalculateResult> getResult(double amountRate, BiFunction<List<DailyData>, Double, DailyData> chooser, PriceAction buyPriceAction, BuyStrategy buyStrategy,
                                                   PriceAction sellPriceAction,
                                                   SellStrategy sellStrategy
    ) throws IOException {
        Map<String, List<BuyStock>> buyStockMap = new LinkedHashMap<>();
        double startMoney = START_MONEY;
        int i = 0;

        Map<String, List<DailyData>> map = ReadUtils.readAllDailyDatas(currentYear);
        for (Map.Entry<String, List<DailyData>> entry : map.entrySet()) {
            List<DailyData> dailyDataList = entry.getValue();
            DailyData dailyData = chooser.apply(dailyDataList, amountRate);    // 需要过滤掉amountRate为0的数据
            if (dailyData == null) {
                continue;
            }
            FiveMinutesBuyAndSell buyFiveMinutesData = getBuyAndSellData(dailyData);
            List<FiveMinutesData> buyList = buyFiveMinutesData.getBuyList();
            List<FiveMinutesData> sellList = buyFiveMinutesData.getSellList();

            // 只要有一条数据为空,则当天空仓
            if (CollectionUtils.isEmpty(buyList) || CollectionUtils.isEmpty(sellList)) {
                continue;
            }

            LinkedHashMap<String, StockData> fiveMinutesBuyMap = getFiveMinutesMap(buyList);
            LinkedHashMap<String, StockData> fiveMinutesSellMap = getFiveMinutesMap(sellList);

            int startHour = 9;
            int startMinute = 40;
            List<String> sellStringList;
            if (buyAtLastMinutes) {
                startHour = 15;
                startMinute = 00;
            }

            if (fixSellPoint) {
                sellStringList = Arrays.asList(new String[]{sellPoint});
            } else {
                sellStringList = TimeLoopUtils.getTimeLoop(9, 35);
            }

            for (String startTime : TimeLoopUtils.getTimeLoop(startHour, startMinute)) {
                for (String endTime : sellStringList) {
                    if (startTime.compareTo(endTime) <= 0) {
                        continue;
                    }
                    String key = startTime + "_" + endTime;
                    List<BuyStock> buyStockList;
                    if (buyStockMap.containsKey(key)) {
                        buyStockList = buyStockMap.get(key);
                    } else {
                        buyStockList = new ArrayList<>();
                    }

                    String buyPriceStr = PriceFinder.findCurrentPriceWithData(fiveMinutesBuyMap, buyFiveMinutesData.getBuyData().convertToStockData(), buyFiveMinutesData.getBuyDate(),
                            startTime, buyPriceAction, buyStrategy
                    );
                    String sellPriceStr = PriceFinder.findNextPriceWithData(fiveMinutesSellMap, buyFiveMinutesData.getSellData().convertToStockData(), buyFiveMinutesData.getSellDate(),
                            endTime, sellPriceAction, sellStrategy
                    );
                    if (buyPriceStr == null || sellPriceStr == null) {
                        continue;
                    }
                    double buyPrice = Double.valueOf(buyPriceStr);
                    double sellPrice = Double.valueOf(sellPriceStr);

                    BuyStock buyStock = new BuyStock(dailyData.getName(),
                            buyFiveMinutesData.getBuyDate() + startTime,
                            buyFiveMinutesData.getSellDate() + endTime,
                            String.valueOf(buyPrice), String.valueOf(sellPrice));
                    // 获取收益率
                    if (i != 0) {
                        BuyStock lastBuyStock = buyStockList.get(buyStockList.size() - 1);
                        startMoney = lastBuyStock.getEndMoney();
                    }
                    buyStock.setStartMoney(startMoney);
                    long amount = (long) Math.floor(startMoney / (buyPrice * 100));   // 买入手数
                    buyStock.setBuyMoney(amount * buyPrice * 100);
                    buyStock.setSellMoney(amount * 100 * sellPrice * (1 - feeLoss));
                    buyStock.setEndMoney(buyStock.getSellMoney() + startMoney - buyStock.getBuyMoney());

                    Double gainRate = buyStock.getEndMoney() / buyStock.getStartMoney() - 1;
                    buyStock.setGainRate(gainRate);

                    buyStock.setFee(buyStock.getSellMoney() * feeLoss);
                    buyStock.setGain(buyStock.getEndMoney() - buyStock.getStartMoney());
                    buyStock.setAmount(amount);
                    buyStockList.add(buyStock);
                    if (!buyStockMap.containsKey(key)) {
                        buyStockMap.put(key, buyStockList);
                    }
                }
            }
            i++;
        }
        List<CalculateResult> resultList = new ArrayList<>();
        for (Map.Entry<String, List<BuyStock>> entry : buyStockMap.entrySet()) {
            String[] key = entry.getKey().split("_");
            CalculateResult result = TimeLoopUtils.getCalculateResult(key[0], key[1], entry.getValue());
            resultList.add(result);
        }
        return resultList;
    }

    private static LinkedHashMap<String, StockData> getFiveMinutesMap(List<FiveMinutesData> sellList) {
        LinkedHashMap<String, StockData> map = new LinkedHashMap<>();
        for (FiveMinutesData data : sellList) {
            map.put(data.getTime(), new StockData(data.getTime(), data.getOpen(), data.getHigh(), data.getLow(), data.getClose()));
        }
        return map;
    }

    private static double getSellPrice(List<FiveMinutesData> sellList, String sellDate, String selTime) {
        String dateTime = getDateTime(sellDate, selTime);
        return sellList.stream().filter(item -> item.getTime().equals(dateTime)).findFirst().get().getOpen();
    }

    @NotNull
    private static String getDateTime(String buyDate, String buyTime) {
        return buyDate.replaceAll("-", "") + buyTime + "00000";
    }

    private static double getBuyPrice(List<FiveMinutesData> buyList, String buyDate, String buyTime) {
        String dateTime = getDateTime(buyDate, buyTime);
        return buyList.stream().filter(item -> item.getTime().equals(dateTime)).findFirst().get().getOpen();
    }

    private static FiveMinutesBuyAndSell getBuyAndSellData(DailyData data) throws IOException {
        String date = data.getDate();

        String nextExchangeDay;
        if (buyAtLastMinutes) {
            nextExchangeDay = date;
        } else {
            nextExchangeDay = ExchangeDay.getNextExchangeDay(date);
        }
        List<FiveMinutesData> fiveMinutesData = new ArrayList<>();
        String fileName = data.getCode() + ".txt";
        List<FiveMinutesData> buyList = null;
        List<FiveMinutesData> sellList = null;
        String buyDate = null;
        String sellDate = null;
        DailyData buyData = null;
        DailyData sellData = null;
        while (nextExchangeDay.startsWith(currentYear)) {
            List<FiveMinutesData> list = ReadUtils.readFiveMinutesData(fiveMinutesDataDir, fileName, nextExchangeDay);
            if (!list.isEmpty()) {
                buyList = list;
                buyDate = nextExchangeDay;
                buyData = ReadUtils.readOneDailyData(ReadUtils.getDailyDataDir(currentYear), fileName, buyDate);
                break;
            }
            nextExchangeDay = ExchangeDay.getNextExchangeDay(nextExchangeDay);
        }
        if (buyList == null) {
            return new FiveMinutesBuyAndSell(null, null);
        }
        nextExchangeDay = ExchangeDay.getNextExchangeDay(nextExchangeDay);

        while (nextExchangeDay.startsWith(currentYear)) {
            List<FiveMinutesData> list = ReadUtils.readFiveMinutesData(fiveMinutesDataDir, fileName, nextExchangeDay);
            if (!list.isEmpty()) {
                sellList = list;
                sellDate = nextExchangeDay;
                sellData = ReadUtils.readOneDailyData(ReadUtils.getDailyDataDir(currentYear), fileName, sellDate);
                break;
            }
            nextExchangeDay = ExchangeDay.getNextExchangeDay(nextExchangeDay);
        }
        FiveMinutesBuyAndSell fiveMinutesBuyAndSell = new FiveMinutesBuyAndSell(buyList, sellList);
        fiveMinutesBuyAndSell.setBuyDate(buyDate);
        fiveMinutesBuyAndSell.setSellDate(sellDate);
        fiveMinutesBuyAndSell.setBuyData(buyData);
        fiveMinutesBuyAndSell.setSellData(sellData);
        return fiveMinutesBuyAndSell;
    }

    public static boolean filterStock(DailyData dailyData) {
        double pctChg = dailyData.getPctChg();
        if (pctChg >= downLimit && pctChg < upLimit) {
            return true;
        }
        return false;
    }
}
